/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ua.epam.muzalevskiy.third_project.XMLParsers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import ua.epam.muzalevskiy.third_project.knife.Blade;
import ua.epam.muzalevskiy.third_project.knife.BladeLength;
import ua.epam.muzalevskiy.third_project.knife.BladeMaterial;
import ua.epam.muzalevskiy.third_project.knife.BladeWidth;
import ua.epam.muzalevskiy.third_project.knife.Handle;
import ua.epam.muzalevskiy.third_project.knife.HandleMaterial;
import ua.epam.muzalevskiy.third_project.knife.Knife;
import ua.epam.muzalevskiy.third_project.knife.KnifeComparator;
import ua.epam.muzalevskiy.third_project.knife.KnifeHandy;
import ua.epam.muzalevskiy.third_project.knife.KnifeParametersSwitcher;
import ua.epam.muzalevskiy.third_project.knife.KnifeType;
import ua.epam.muzalevskiy.third_project.knife.Units;

/**
 * This class realized logic of <code> DOMParser </code> work. 
 *
 * @author Muzalevskiy Vitaliy
 */
public class DomParser implements XMLParser{
    
    /**
     * <code> List </code> of <code> Knife </code> type values that parsed 
     * from XML file.
     */
    private List<Knife> knifeList = new ArrayList();

    @Override
    public List<Knife> getKnifeList() {
        Collections.sort(knifeList, new KnifeComparator());
        return knifeList;
    }
    
    @Override
    public void parse(String fileName){
        try{
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(fileName);
            Element root = document.getDocumentElement();
            parseKnifeList(root.getChildNodes());
            
        } catch( ParserConfigurationException | SAXException | IOException e){
            System.err.println(e.getMessage());
        }
    }
    
    /**
     * This method get from XML file all parameters of <code> Knife </code> 
     * and form new Java object from them, then add this object to 
     * <code> ArrayList </code>.
     * 
     * @param knifeListNodes <code> List </code> of Nodes that include 
     * information about knives stored in XML file.
     */
    private void parseKnifeList(NodeList knifeListNodes){
        for(int i = 0; i < knifeListNodes.getLength(); i++){
            Node node = knifeListNodes.item(i);
            if(node.getNodeType() == Node.ELEMENT_NODE){
                Element element = (Element)node;
                String id = element.getAttribute("id");
                KnifeType type = getKnifeType(element);
                KnifeHandy handy = getKnifeHandy(element);
                String origin = ((Element)(element.getElementsByTagName("origin")
                        .item(0))).getFirstChild().getNodeValue();
                Blade blade = getBladeInfo(element);
                Handle handle = getHandleInfo(element);
                boolean value = Boolean.valueOf(((Element)(element
                        .getElementsByTagName("value").item(0))).getFirstChild()
                        .getNodeValue());
                Knife knife = new Knife(id, type, handy, 
                        origin, blade, handle, value);
                knifeList.add(knife);
            }
        }
    }
    
    /**
     * Get from XML file type of concrete knife.
     * 
     * @param element element of DOM that describe current knife.
     * @return value of type of the knife.
     */
    private KnifeType getKnifeType(Element element){
        String value = ((Element)(element.getElementsByTagName("type").item(0)))
                .getFirstChild().getNodeValue();
        KnifeType result = KnifeParametersSwitcher.swithKnifeType(value);
        return result;
    }
    
    /**
     * Get from XML file handy value of concrete knife.
     * 
     * @param element element of DOM that describe current knife.
     * @return handy parameter of current knife
     */
    private KnifeHandy getKnifeHandy(Element element){
        String handy = ((Element)(element.getElementsByTagName("handy")
                .item(0))).getFirstChild().getNodeValue();
        KnifeHandy result = KnifeParametersSwitcher.switchKnifeHandy(handy);
        return result;
    }
    
    /**
     * Get from XML file all information about blade of concrete knife.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> Blade </code> type.
     */
    private Blade getBladeInfo(Element element){
        Blade blade;
        BladeLength length = getBladeLength(element);
        BladeWidth width = getBladeWidth(element);
        BladeMaterial material = getBladeMaterial(element);
        blade = new Blade(length, width, material);
        return blade;
    }
    
    /**
     * Get from XML file value of knife blade length.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> BladeLength </code> type.
     */
    private BladeLength getBladeLength(Element element){
        BladeLength result;
        int length = Integer.parseInt(((Element)(element
                .getElementsByTagName("length").item(0)))
                .getFirstChild().getNodeValue());
        Units units = getLengthUnits(element);
        result = new BladeLength(units, length);
        return result;
    }
    
    /**
     * Get from XML file units value of knife blade length.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> Units </code> type.
     */
    private Units getLengthUnits(Element element){
        String units = ((Element)(element.getElementsByTagName("length").item(0)))
                .getAttributes().getNamedItem("units").getFirstChild().getNodeValue();
        Units result = KnifeParametersSwitcher.switchUnits(units);
        return result;
    }
    
    /**
     * Get from XML file value of knife blade width.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> BladeWidth </code> type.
     */
    private BladeWidth getBladeWidth(Element element){
        BladeWidth result;
        int width = Integer.parseInt(((Element)(element.getElementsByTagName("width")
                .item(0))).getFirstChild().getNodeValue());
        Units units = getWidthUnits(element);
        result = new BladeWidth(units, width);
        return result;
    }
    
    /**
     * Get from XML file units value of knife blade width.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> Units </code> type.
     */
    private Units getWidthUnits(Element element){
        String units = ((Element)(element.getElementsByTagName("width").item(0)))
                .getAttributes().getNamedItem("units").getFirstChild()
                .getNodeValue();
        Units result = KnifeParametersSwitcher.switchUnits(units);
        return result;
    }
    
    /**
     * Get from XML file value of material of knife blade.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> BladeMaterial </code> type.
     */
    private BladeMaterial getBladeMaterial(Element element){
        Element blade = (Element)element.getElementsByTagName("blade").item(0);
        String material = ((Element)(blade.getElementsByTagName("material")
                .item(0))).getFirstChild().getNodeValue();
        BladeMaterial result = KnifeParametersSwitcher.switchBladeMaterial(material);
        return result;
    }
    
    /**
     * Get from XML file all information about knife handle.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> Handle </code> type.
     */
    private Handle getHandleInfo(Element element){
        Handle result;
        HandleMaterial material = getHandleMaterial(element);
        boolean bloodFlow = Boolean.valueOf(((Element)(element
                .getElementsByTagName("bloodFlow").item(0))).getFirstChild()
                .getNodeValue());
        result = new Handle(material, bloodFlow);
        return result;
    }
    
    /**
     * Get from XML file value of material of knife handle.
     * 
     * @param element element of DOM that describe current knife.
     * @return object of <code> HandleMaterial </code> type.
     */
    private HandleMaterial getHandleMaterial(Element element){
        Element handle = (Element)element.getElementsByTagName("handle").item(0);
        String material = ((Element)(handle.getElementsByTagName("material")
                .item(0))).getFirstChild().getNodeValue();
        HandleMaterial result = KnifeParametersSwitcher.switchHandleMaterial(material);
        return result;
    }
}
